package com.gitee.grower

import ac.github.oa.OriginAttribute
import ac.github.oa.api.common.Matcher
import ac.github.oa.internal.core.item.ItemPlant
import com.gitee.grower.core.kether.*
import com.gitee.grower.core.scheme.Curse
import com.gitee.grower.core.scheme.Item
import com.gitee.grower.core.scheme.Operation
import com.gitee.grower.core.scheme.Scheme
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import taboolib.common.platform.function.info
import taboolib.common.util.random
import taboolib.module.configuration.Configuration
import taboolib.module.nms.ItemTagData
import taboolib.module.nms.getItemTag
import taboolib.platform.util.*
import java.io.File

object ItemGrowerAPI {

    val schemes = mutableMapOf<String, Scheme>()

    val fortuneCurses = mutableListOf<Curse.Fortune>()

    val perfectCurses = mutableListOf<Curse.Perfect>()

    const val NBT_ITEM_GROWER = "@ItemGrower"

    fun getFortuneCurse(itemStack: ItemStack?): Curse.Fortune? {
        val item = ItemPlant.parseItem(itemStack) ?: return null
        return fortuneCurses.firstOrNull { it.id == item.key }
    }

    fun getPerfectCurse(itemStack: ItemStack?): Curse.Perfect? {
        val item = ItemPlant.parseItem(itemStack) ?: return null
        return perfectCurses.firstOrNull { it.id == item.key }
    }

    fun registerScheme(file: File) {
        schemes[file.nameWithoutExtension] = Scheme(Configuration.loadFromFile(file))
    }

    fun getScheme(player: Player, itemStack: ItemStack): Scheme? {
        if (itemStack.isAir()) return null
        return schemes.values.firstOrNull { toKetherBool(player, it.match, itemStack) }
    }

    fun getStructure(player: Player, itemStack: ItemStack, scheme: Scheme): Scheme.Structure? {
        val index = itemStack.getItemTag()[NBT_ITEM_GROWER]?.asInt() ?: 0
        return scheme.structures.firstOrNull { index in it.index }
    }

    fun modifyItemGrow(player: Player,itemStack: ItemStack) {
        modifyItemGrow(player,itemStack, getScheme(player, itemStack) ?: return)
    }

    fun modifyItemGrow(player: Player,itemStack: ItemStack,scheme: Scheme) {
        modifyItemGrow(player, itemStack, scheme,getStructure(player, itemStack, scheme) ?: return)
    }

    fun modifyItemGrow(player: Player,itemStack: ItemStack,operation: Operation) {
        val scheme = getScheme(player, itemStack) ?: return
        val structure = getStructure(player, itemStack, scheme) ?: return
        modifyItemGrow(player, itemStack, scheme, structure,operation)
    }

    fun modifyItemGrow(player: Player,itemStack: ItemStack,scheme: Scheme,structure: Scheme.Structure) {
        val type = if (random(toKetherDouble(player,structure.chance,itemStack))) {
            Operation.INCREASE
        } else {
            Operation.DECREASE
        }
        modifyItemGrow(player, itemStack, scheme, structure, type)
    }


    fun modifyItemGrow(player: Player, itemStack: ItemStack, scheme: Scheme,structure: Scheme.Structure,type: Operation) : Map<String,Double> {
        // 计算累加属性
        val map = structure.attributes.associate {
            it.keyword to if (type == Operation.INCREASE) it.get(player,itemStack) else -it.get(player,itemStack)
        }.toMutableMap()

        val acceptMap = mutableMapOf<String,Double>()

        itemStack.modifyLore {
            // 如果前缀为空 则为从第一行还是检索注入
            var flag = ItemGrower.checkPrefix == null
            this.forEachIndexed { index, s ->

                // 注入值
                if (flag) {
                    val mapGetKeyword = mapGetKeyword(map, s)
                    if (mapGetKeyword != null) {
                        acceptMap[s] = mapGetKeyword.value
                        this[index] = intoAddon(s, mapGetKeyword.value)
                        map.remove(mapGetKeyword.key)
                        return@forEachIndexed
                    }
                }
                // 截获范围
                if (ItemGrower.checkPrefix != null && s.contains(ItemGrower.checkPrefix!!)) {
                    flag = true
                    return@forEachIndexed
                }

                if (ItemGrower.checkSuffix != null && s.contains(ItemGrower.checkSuffix!!)) {
                    flag = false
                    return@forEachIndexed
                }
            }
        }

        if (type == Operation.INCREASE) {
            autoIncreaseGrowLevel(itemStack)
        } else {
            autoDecreaseGrowLevel(itemStack)
        }
        return acceptMap
    }



    fun modifyNegativeItem(player: Player,itemStack: ItemStack,scheme: Scheme,structure: Scheme.Structure,negativeString: String) {
        itemStack.modifyLore {
            val indexOf = this.indexOfFirst { it.contains(ItemGrower.negativePrefix) }
            if (indexOf != -1) {
                this.add(this.indexOfFirst { it.contains(ItemGrower.negativeSuffix) },negativeString)
            } else {
                this += ItemGrower.negativePrefix
                this += negativeString
                this += ItemGrower.negativeSuffix
            }
        }
    }

    fun autoIncreaseGrowLevel(itemStack: ItemStack) {
        updateGrowLevel(itemStack, itemStack.growLevel() + 1)
    }

    fun autoDecreaseGrowLevel(itemStack: ItemStack) {
        val level = itemStack.growLevel()
        if (level == 0) return
        updateGrowLevel(itemStack, level - 1)
    }

    fun updateGrowLevel(itemStack: ItemStack, level: Int) {
        val itemTag = itemStack.getItemTag()
        itemTag[NBT_ITEM_GROWER] = ItemTagData(level)
        itemTag.saveTo(itemStack)
    }

    fun ItemStack.growLevel(): Int {
        val itemTag = this.getItemTag()
        return itemTag[NBT_ITEM_GROWER]?.asInt() ?: 0
    }

    fun Scheme.Structure.takeoutItem(player: Player,itemStack: ItemStack) {
        val map = this.items.associate { it to it.get(player, itemStack) }
        map.forEach {
            val item = it.key
            val itemId = item.itemId()
            if (item is Item.Default) {
                player.inventory.takeItem(it.value) { ItemPlant.parseItem(it)?.key == itemId }
            } else if (item is Item.Condition) {
                player.inventory.takeItem(it.value) { ItemPlant.parseItem(it)?.key == itemId && toKetherBool(player,item.match,it) }
            }
        }
    }

    fun mapGetKeyword(map: Map<String, Double>, parse: String): Map.Entry<String, Double>? {
        map.forEach {
            if (parse.contains(it.key)) {
                return it
            }
        }
        return null
    }

    fun intoAddon(string: String, value: Double): String {
        val matcher = Matcher.parse(string)
        if (matcher is Matcher.Single) {
            return addonIncrease(string, value, matcher)
        }
        if (matcher is Matcher.Range) {
            return string.replaceFirst(matcher.pos1.parse, addonIncrease(string, value, matcher.pos1))
                .replaceAfterLast(matcher.pos2.parse, addonIncrease(string, value, matcher.pos2))
        }
        return string
    }

    fun addonIncrease(string: String, value: Double, matcher: Matcher.Single): String {
        matcher.extraAmount += value
        return string.replace(matcher.main.toString(), toCommonValue(matcher.extraAmount))
    }

    fun toCommonValue(value: Double): String {
        return if (value > 0) {
            "+${OriginAttribute.decimalFormat.format(value)}"
        } else {
            OriginAttribute.decimalFormat.format(value)
        }
    }

    fun checkMisMatchItem(player: Player,itemStack: ItemStack, scheme: Scheme, structure: Scheme.Structure): MutableList<Item> {
        val list = mutableListOf<Item>()
        structure.items.forEach { item ->
            val countItem = item.countItem(player)
            info("${item.itemId()} count $countItem")
            if (countItem < item.get(player,itemStack)) {
                list += item
            }
        }
        return list
    }

    fun Item.itemId(): String {
        return when(this) {
            is Item.Default -> this.id
            is Item.Condition -> this.iconId
            else -> error("Item $this No id.")
        }
    }

    fun Item.countItem(player: Player): Int {
        val itemId = this.itemId()

        if (this is Item.Default) {
            return player.inventory.countItem { ItemPlant.parseItem(it)?.key == itemId }
        } else if (this is Item.Condition) {
            return player.inventory.countItem {
                ItemPlant.parseItem(it)?.key == itemId && toKetherBool(player,this.match,it)
            }
        }

        return 0
    }

    fun Item.checkCondition(player: Player,itemStack: ItemStack): Boolean {
        return when (this) {

            is Item.Default -> {
                player.inventory.hasItem(get(player,itemStack)) { ItemPlant.parseItem(it)?.key == this.id }
            }

            is Item.Condition -> {
                player.inventory.hasItem(get(player,itemStack)) {
                    ItemPlant.parseItem(it)?.key == this.iconId && toKetherBool(player, this.match, it)
                }
            }

            else -> false
        }
    }

    fun Item.get(player: Player,itemStack: ItemStack): Int {
        return toKetherInt(player, this.amountScript,itemStack)
    }

    fun Scheme.Attribute.get(player: Player,itemStack: ItemStack): Double {
        return toKetherDouble(player, this.valueScript,itemStack)
    }


}